<template>
  <div ref="canvas"></div>
</template>

<script lang="ts" setup>
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import { GUI } from "three/examples/jsm/libs/lil-gui.module.min.js";
import Stats from "three/examples/jsm/libs/stats.module";
import {ref, onMounted, render} from "vue";
import {addHouseAndTree} from "@/book/ThreeUtils";

// 画布
const canvas = ref<any>();
// 场景
const scene = new THREE.Scene();
// 相机
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(-30, 40, 30);
camera.lookAt(0, 0, 0);
// 渲染器
const renderer = new THREE.WebGLRenderer({
  antialias: true,
});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(0x000000);
renderer.setPixelRatio(window.devicePixelRatio);
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
// 开启物理正确光源模拟现实世界
renderer.physicallyCorrectLights = true;
// 相机控件
const orbitControl = new OrbitControls(camera, renderer.domElement);
// 性能统计
const stat = new Stats();

addHouseAndTree(scene);

// 环境光
// distance - 光源照射的距离，默认值为0，意味着光的强度不会随着距离增加而减少
const ambient = new THREE.AmbientLight("#0c0c0c");
scene.add(ambient);
// 点光源
const point = new THREE.PointLight("#ccffcc");
// 衰减 - 光源强度随着离开光源的距离而衰减的速度。为 2 时更接近现实世界的效果，默认值为1，但是只有当WebGLRenderer的属性physicallyCorrectLights为启用时
//       才会生效
point.decay = 0.1;
// 功率 - 当WebGLRenderer的属性physicallyCorrectLights为启用时，可以使用该属性指定光源的功率，默认值为 4 * Math.PI，它与intensity为线性关系
//       power = intensity * 4 * Math.PI
point.power = 4 * Math.PI;
point.castShadow = true;
scene.add(point);
// 辅助坐标系
const pointHelper = new THREE.PointLightHelper(point);
scene.add(pointHelper);
const shadowHelper = new THREE.CameraHelper(point.shadow.camera);
scene.add(shadowHelper);
// 点光源模拟
const pointLightSphereGeometry = new THREE.SphereGeometry(0.2);
const pointLightSphereMaterial = new THREE.MeshBasicMaterial({
  color: 0xac6c25,
});
const pointLightSphere = new THREE.Mesh(pointLightSphereGeometry, pointLightSphereMaterial);
pointLightSphere.position.set(3, 0, 5);
scene.add(pointLightSphere);
// gui
const controlObj = {
  rotationSpeed: 0.01,
  bouncingSpeed: 0.03,
  ambientColor: ambient.color.getStyle(),
  pointColor: point.color.getStyle(),
  intensity: 1,
  distance: point.distance,
  decay: point.decay,
  power: point.power,
};
const gui = new GUI();
gui.addColor(controlObj, 'ambientColor').name("环境光颜色").onChange((e) => {
  ambient.color = new THREE.Color(e);
});
gui.addColor(controlObj, 'pointColor').name("点光源颜色").onChange((e) => {
  point.color = new THREE.Color(e);
});
// @ts-ignore
gui.add(controlObj, 'distance', 0, 100).name("点光源距离").onChange((e) => {
  point.distance = e;
});
// @ts-ignore
gui.add(controlObj, 'intensity', 0, 3).name("点光源强度").onChange((e) => {
  point.intensity = e;
});
// @ts-ignore
gui.add(controlObj, 'decay', 0, 2).name("点光源衰减").step(0.01).onChange((e) => {
  point.decay = e;
});
// @ts-ignore
gui.add(controlObj, 'power', 4, 8).name("点光源功率").step(0.5).onChange((e) => {
  point.power = e * Math.PI;
});

let invert = 1;
let phase = 0;
// 渲染函数
const render = () => {
  stat.update();
  orbitControl.update();
  pointHelper.update();
  shadowHelper.update();

  point.position.copy(pointLightSphere.position);

  if (phase > 2 * Math.PI) {
    invert *= -1;
    phase -= 2 * Math.PI;
  } else {
    phase += controlObj.rotationSpeed;
  }

  pointLightSphere.position.x = +(14 * (Math.cos(phase)));
  pointLightSphere.position.z = +(25 * (Math.sin(phase)));
  pointLightSphere.position.y = 5;

  if (invert < 0) {
    const pivot = 14;
    pointLightSphere.position.x = (invert * (pointLightSphere.position.x - pivot)) + pivot;
  }

  requestAnimationFrame(render);
  renderer.render(scene, camera);
};

onMounted(() => {
  canvas.value.appendChild(stat.domElement);
  canvas.value.appendChild(renderer.domElement);

  render();
});

</script>
